<template>
  <div class="upload-download-container">
    <div class="header">
      <h1>断点续传上传下载</h1>
    </div>
    
    <div class="content">
      <!-- 上传部分 -->
      <div class="section">
        <h2>文件上传</h2>
        <div class="upload-area">
          <input 
            type="file" 
            ref="fileInput"
            style="display: none"
            @change="handleFileSelect"
          />
          <nut-button type="primary" @click="triggerFileSelect">选择文件</nut-button>
          
          <div v-if="uploadFile" class="file-info">
            <p>{{ uploadFile.name }}</p>
            <p>{{ formatFileSize(uploadFile.size) }}</p>
            <div class="progress-container">
              <div 
                class="progress-bar" 
                :style="{ width: uploadProgress + '%' }"
              ></div>
            </div>
            <p class="progress-text">{{ uploadProgress }}%</p>
            <div class="upload-controls">
              <nut-button 
                type="primary" 
                size="small" 
                @click="startUpload" 
                :disabled="isUploading"
              >
                {{ isUploading ? '上传中...' : '开始上传' }}
              </nut-button>
              <nut-button 
                type="default" 
                size="small" 
                @click="pauseUpload" 
                :disabled="!isUploading"
              >
                暂停上传
              </nut-button>
              <nut-button 
                type="danger" 
                size="small" 
                @click="cancelUpload"
              >
                取消上传
              </nut-button>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 下载部分 -->
      <div class="section">
        <h2>文件下载</h2>
        <div class="download-area">
          <nut-input 
            v-model="downloadFileName" 
            placeholder="输入要下载的文件名"
            class="download-input"
          />
          <nut-button type="primary" @click="startDownload" :disabled="isDownloading">
            {{ isDownloading ? '下载中...' : '开始下载' }}
          </nut-button>
          
          <div v-if="downloadInfo" class="download-info">
            <p>{{ downloadInfo.fileName }}</p>
            <p>{{ formatFileSize(downloadInfo.size) }}</p>
            <div class="progress-container">
              <div 
                class="progress-bar" 
                :style="{ width: downloadProgress + '%' }"
              ></div>
            </div>
            <p class="progress-text">{{ downloadProgress }}%</p>
            <div class="download-controls">
              <nut-button 
                type="primary" 
                size="small" 
                @click="resumeDownload" 
                :disabled="isDownloading"
              >
                继续下载
              </nut-button>
              <nut-button 
                type="default" 
                size="small" 
                @click="pauseDownload" 
                :disabled="!isDownloading"
              >
                暂停下载
              </nut-button>
              <nut-button 
                type="danger" 
                size="small" 
                @click="cancelDownload"
              >
                取消下载
              </nut-button>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 上传历史记录 -->
      <div class="section">
        <h2>上传历史</h2>
        <div class="history-list">
          <div 
            v-for="file in uploadHistory" 
            :key="file.id"
            class="history-item"
          >
            <p>{{ file.name }}</p>
            <p>{{ formatFileSize(file.size) }}</p>
            <p>{{ file.status }}</p>
            <nut-button type="default" size="small" @click="resumeUploadHistory(file)">
              {{ file.status === '上传中' ? '继续上传' : '重新上传' }}
            </nut-button>
          </div>
          <p v-if="uploadHistory.length === 0" class="empty-text">暂无上传历史</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { showToast } from '@nutui/nutui'


// 文件上传相关
const fileInput = ref<HTMLInputElement>()
const uploadFile = ref<File | null>(null)
const uploadProgress = ref(0)
const isUploading = ref(false)
const uploadHistory = reactive<any[]>([])

// 文件下载相关
const downloadFileName = ref('')
const downloadInfo = ref<any>(null)
const downloadProgress = ref(0)
const isDownloading = ref(false)

// 断点续传相关
const uploadChunks = reactive<Map<string, { chunk: Blob; index: number }>>(new Map())
const uploadChunkSize = 1024 * 1024 * 2 // 2MB 一个分片
const uploadAbortController = ref<AbortController | null>(null)

const downloadChunks = reactive<Map<number, ArrayBuffer>>(new Map())
const downloadChunkSize = 1024 * 1024 * 2 // 2MB 一个分片
const downloadAbortController = ref<AbortController | null>(null)

// 触发文件选择
const triggerFileSelect = () => {
  fileInput.value?.click()
}

// 处理文件选择
const handleFileSelect = (event: Event) => {
  const target = event.target as HTMLInputElement
  if (target.files && target.files.length > 0) {
    uploadFile.value = target.files[0]
    uploadProgress.value = 0
    // 检查是否有未完成的上传记录
    checkUploadStatus()
  }
}

// 计算文件MD5作为唯一标识
const calculateFileMD5 = async (file: File): Promise<string> => {
  return new Promise((resolve) => {
    // 模拟MD5计算，实际项目中应该使用专门的库
    const md5 = `md5_${file.name}_${file.size}_${file.lastModified}`
    resolve(md5)
  })
}

// 检查上传状态
const checkUploadStatus = async () => {
  if (!uploadFile.value) return
  
  try {

    const fileMD5 = await calculateFileMD5(uploadFile.value)
    
    // 1. 从localStorage中恢复上传状态
    const savedProgress = localStorage.getItem(`upload_progress_${fileMD5}`)
    if (savedProgress) {
      uploadProgress.value = parseInt(savedProgress)
      showToast.success('发现未完成的上传任务，可以继续上传')
    }
    
    // 2. 实际项目中应该调用后端API检查已上传的分片
    // 这里模拟检查已上传的分片
    const uploadedChunksKey = `uploaded_chunks_${fileMD5}`
    const savedChunks = localStorage.getItem(uploadedChunksKey)
    if (savedChunks) {
      const chunks = JSON.parse(savedChunks)
      chunks.forEach((chunk: number) => {
        uploadChunks.set(`${chunk}`, { chunk: new Blob(), index: chunk })
      })
    }
  } catch (error) {
    console.error('检查上传状态失败:', error)
  }
}

// 开始上传
const startUpload = async () => {
  if (!uploadFile.value || isUploading.value) return
  
  isUploading.value = true
  try {
    await uploadFileWithResume()
  } catch (error) {
      if ((error as Error).name !== 'AbortError') {
        console.error('上传失败:', error)
      showToast.fail('上传失败，请重试')
    }
  } finally {
    isUploading.value = false
  }
}

// 暂停上传
const pauseUpload = async () => {
  if (uploadAbortController.value) {
    uploadAbortController.value.abort()
    uploadAbortController.value = null
    isUploading.value = false
    showToast.text('上传已暂停')
    
    // 保存上传进度
    if (uploadFile.value) {
      const fileMD5 = await calculateFileMD5(uploadFile.value)
      localStorage.setItem(`upload_progress_${fileMD5}`, uploadProgress.value.toString())
      
      // 保存到上传历史
      const existingIndex = uploadHistory.findIndex(item => item.md5 === fileMD5)
      const historyItem = {
        id: existingIndex >= 0 ? uploadHistory[existingIndex].id : Date.now().toString(),
        name: uploadFile.value.name,
        size: uploadFile.value.size,
        status: '上传中',
        md5: fileMD5,
        progress: uploadProgress.value
      }
      
      if (existingIndex >= 0) {
        uploadHistory[existingIndex] = historyItem
      } else {
        uploadHistory.push(historyItem)
      }
      localStorage.setItem('upload_history', JSON.stringify(uploadHistory))
    }
  }
}

// 取消上传
const cancelUpload = async () => {
  if (uploadFile.value) {
    const fileMD5 = await calculateFileMD5(uploadFile.value)
    
    // 清除保存的进度和分片信息
    localStorage.removeItem(`upload_progress_${fileMD5}`)
    localStorage.removeItem(`uploaded_chunks_${fileMD5}`)
    
    // 从上传历史中移除
    const index = uploadHistory.findIndex(item => item.md5 === fileMD5)
    if (index >= 0) {
      uploadHistory.splice(index, 1)
      localStorage.setItem('upload_history', JSON.stringify(uploadHistory))
    }
  }
  
  pauseUpload()
  uploadFile.value = null
  uploadProgress.value = 0
  uploadChunks.clear()
  if (fileInput.value) {
    fileInput.value.value = ''
  }
  showToast.text('上传已取消')
}

// 继续上传历史记录
const resumeUploadHistory = async (_file: any) => {
  // 触发文件选择，用户需要重新选择同一个文件
  fileInput.value?.click()
  
  // 实际项目中可以考虑直接从历史记录恢复上传
  // 但由于浏览器安全限制，通常需要用户手动选择文件
  showToast.text('请重新选择相同的文件以继续上传')
}

// 获取文件信息
const getFileInfo = async (fileName: string): Promise<any> => {
  try {
    // 实际项目中应该调用后端API获取文件信息
    // const response = await request.get('/api/download/info', {
    //   params: { fileName }
    // })
    // return response.data
    
    // 模拟文件信息
    return {
      fileName,
      size: 1024 * 1024 * 50, // 50MB 模拟大小
      id: Date.now().toString(),
      md5: `download_md5_${fileName}_${Date.now()}`
    }
  } catch (error) {
    console.error('获取文件信息失败:', error)
    throw error
  }
}

// 检查下载状态
const checkDownloadStatus = async (fileName: string): Promise<number> => {
  try {
    // 从localStorage中恢复下载状态
    const savedProgress = localStorage.getItem(`download_progress_${fileName}`)
    if (savedProgress) {
      const progress = parseInt(savedProgress)
      showToast.success('发现未完成的下载任务，可以继续下载')
      return progress
    }
    return 0
  } catch (error) {
    console.error('检查下载状态失败:', error)
    return 0
  }
}

// 开始下载
const startDownload = async () => {
  if (!downloadFileName.value || isDownloading.value) return
  
  isDownloading.value = true
  try {
    // 获取文件信息
    downloadInfo.value = await getFileInfo(downloadFileName.value)
    
    // 检查下载状态
    downloadProgress.value = await checkDownloadStatus(downloadInfo.value.fileName)
    
    await downloadFileWithResume()
  } catch (error) {
    if ((error as Error).name !== 'AbortError') {
      console.error('下载失败:', error)
      showToast.fail('下载失败，请重试')
    }
  } finally {
    isDownloading.value = false
  }
}

// 暂停下载
const pauseDownload = () => {
  if (downloadAbortController.value) {
    downloadAbortController.value.abort()
    downloadAbortController.value = null
    isDownloading.value = false
    showToast.text('下载已暂停')
    
    // 保存下载进度
    if (downloadInfo.value) {
      localStorage.setItem(`download_progress_${downloadInfo.value.fileName}`, downloadProgress.value.toString())
      
      // 保存已下载的分片信息
      const downloadedChunksKey = `downloaded_chunks_${downloadInfo.value.fileName}`
      const downloadedChunks = Array.from(downloadChunks.keys())
      localStorage.setItem(downloadedChunksKey, JSON.stringify(downloadedChunks))
    }
  }
}

// 继续下载
const resumeDownload = async () => {
  if (!downloadInfo.value || isDownloading.value) return
  
  isDownloading.value = true
  try {
    // 恢复已下载的分片信息
    const downloadedChunksKey = `downloaded_chunks_${downloadInfo.value.fileName}`
    const savedChunks = localStorage.getItem(downloadedChunksKey)
    if (savedChunks) {
      const chunks = JSON.parse(savedChunks)
      chunks.forEach((_chunkIndex: number) => {
        // 这里我们不实际恢复分片数据，只记录已下载的分片索引
        // 实际项目中可能需要更复杂的处理
      })
    }
    
    await downloadFileWithResume()
  } catch (error) {
      if ((error as Error).name !== 'AbortError') {
        console.error('下载失败:', error)
        showToast.fail('下载失败，请重试')
      }
  } finally {
    isDownloading.value = false
  }
}

// 取消下载
const cancelDownload = () => {
  // 清除保存的进度和分片信息
  if (downloadInfo.value) {
    localStorage.removeItem(`download_progress_${downloadInfo.value.fileName}`)
    localStorage.removeItem(`downloaded_chunks_${downloadInfo.value.fileName}`)
  }
  
  pauseDownload()
  downloadInfo.value = null
  downloadProgress.value = 0
  downloadChunks.clear()
  showToast.text('下载已取消')
}

// 上传文件（断点续传）
const uploadFileWithResume = async () => {
  if (!uploadFile.value) return
  
  const fileMD5 = await calculateFileMD5(uploadFile.value)
  uploadAbortController.value = new AbortController()
  const totalChunks = Math.ceil(uploadFile.value.size / uploadChunkSize)
  let uploadedChunks = Math.ceil((uploadProgress.value / 100) * totalChunks)
  
  // 1. 首先向服务器询问已上传的分片
  try {
    // 实际项目中应该调用后端API
    // const response = await request.get('/api/upload/check', {
    //   params: { md5: fileMD5, fileName: uploadFile.value.name }
    // })
    // const { uploadedChunks: serverUploadedChunks } = response.data
    
    // 模拟服务器返回已上传的分片
    const uploadedChunksKey = `uploaded_chunks_${fileMD5}`
    const savedChunks = localStorage.getItem(uploadedChunksKey)
    const serverUploadedChunks = savedChunks ? JSON.parse(savedChunks) : []
    
    // 更新已上传的分片信息
    serverUploadedChunks.forEach((chunkIndex: number) => {
      uploadChunks.set(`${chunkIndex}`, { chunk: new Blob(), index: chunkIndex })
    })
    
    uploadedChunks = serverUploadedChunks.length
    uploadProgress.value = Math.floor((uploadedChunks / totalChunks) * 100)
  } catch (error) {
    console.error('检查已上传分片失败:', error)
  }
  
  // 2. 上传未上传的分片
  for (let i = 0; i < totalChunks; i++) {
    if (!uploadAbortController.value) break
    
    // 跳过已上传的分片
    if (uploadChunks.has(`${i}`)) {
      continue
    }
    
    const start = i * uploadChunkSize
    const end = Math.min(start + uploadChunkSize, uploadFile.value.size)
    const chunk = uploadFile.value.slice(start, end)
    
    try {
      // 准备表单数据
      const formData = new FormData()
      formData.append('file', chunk)
      formData.append('md5', fileMD5)
      formData.append('fileName', uploadFile.value.name)
      formData.append('chunkIndex', i.toString())
      formData.append('totalChunks', totalChunks.toString())
      formData.append('chunkSize', uploadChunkSize.toString())
      
      // 实际项目中应该调用后端API上传分片
      // await request.post('/api/upload/chunk', formData, {
      //   headers: { 'Content-Type': 'multipart/form-data' },
      //   signal: uploadAbortController.value?.signal
      // })
      
      // 模拟上传过程
      await new Promise(resolve => setTimeout(resolve, 1000))
      
      // 保存已上传的分片信息
      uploadChunks.set(`${i}`, { chunk, index: i })
      
      // 保存上传进度
      const currentChunks = Array.from(uploadChunks.keys()).map(Number)
      localStorage.setItem(`uploaded_chunks_${fileMD5}`, JSON.stringify(currentChunks))
      
      uploadedChunks++
      uploadProgress.value = Math.floor((uploadedChunks / totalChunks) * 100)
      localStorage.setItem(`upload_progress_${fileMD5}`, uploadProgress.value.toString())
      
      showToast.success(`分片 ${i + 1}/${totalChunks} 上传成功`)
    } catch (error) {
      throw error
    }
  }
  
  // 3. 上传完成后合并分片
  if (uploadAbortController.value && uploadProgress.value === 100) {
    try {
      // 实际项目中应该调用后端API合并分片
      // await request.post('/api/upload/merge', {
      //   md5: fileMD5,
      //   fileName: uploadFile.value.name,
      //   totalChunks: totalChunks
      // })
      
      showToast.success('文件上传成功')
      
      // 保存到上传历史
      const historyItem = {
        id: Date.now().toString(),
        name: uploadFile.value.name,
        size: uploadFile.value.size,
        status: '上传完成',
        md5: fileMD5,
        uploadTime: new Date().toISOString()
      }
      uploadHistory.push(historyItem)
      localStorage.setItem('upload_history', JSON.stringify(uploadHistory))
      
      // 清除临时保存的进度信息
      localStorage.removeItem(`upload_progress_${fileMD5}`)
      localStorage.removeItem(`uploaded_chunks_${fileMD5}`)
    } catch (error) {
      console.error('合并分片失败:', error)
      showToast.fail('文件上传成功但合并失败')
    }
  }
}

// 下载文件分片
const downloadChunk = async (_fileName: string, start: number, end: number, _chunkIndex: number): Promise<ArrayBuffer> => {
  try {
    // 实际项目中应该调用后端API下载分片
    // const response = await request.get('/api/download/chunk', {
    //   params: { fileName, start, end },
    //   responseType: 'arraybuffer',
    //   headers: { Range: `bytes=${start}-${end-1}` },
    //   signal: downloadAbortController.value?.signal
    // })
    // return response.data
    
    // 模拟下载分片
    await new Promise(resolve => setTimeout(resolve, 1000))
    // 创建一个模拟的ArrayBuffer
    const buffer = new ArrayBuffer(end - start)
    return buffer
  } catch (error) {
    throw error
  }
}

// 合并下载的分片
const mergeChunks = async (_fileName: string): Promise<Blob> => {
  try {
    // 收集所有分片并按顺序合并
    const chunks = Array.from(downloadChunks.entries())
      .sort(([a], [b]) => Number(a) - Number(b))
      .map(([_, chunk]) => chunk)
    
    // 创建Blob对象
    const blob = new Blob(chunks)
    return blob
  } catch (error) {
    console.error('合并分片失败:', error)
    throw error
  }
}

// 保存下载的文件
const saveDownloadedFile = async (blob: Blob, fileName: string) => {
  try {
    // 创建下载链接
    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = fileName
    document.body.appendChild(link)
    link.click()
    
    // 清理
    setTimeout(() => {
      document.body.removeChild(link)
      URL.revokeObjectURL(url)
    }, 100)
  } catch (error) {
    console.error('保存文件失败:', error)
    throw error
  }
}

// 下载文件（断点续传）
const downloadFileWithResume = async () => {
  if (!downloadInfo.value) return
  
  downloadAbortController.value = new AbortController()
  const totalChunks = Math.ceil(downloadInfo.value.size / downloadChunkSize)
  let downloadedBytes = Math.floor((downloadProgress.value / 100) * downloadInfo.value.size)
  let downloadedChunkCount = 0
  
  // 1. 恢复已下载的分片信息
  const downloadedChunksKey = `downloaded_chunks_${downloadInfo.value.fileName}`
  const savedChunks = localStorage.getItem(downloadedChunksKey)
  const downloadedChunkIndexes = savedChunks ? JSON.parse(savedChunks) : []
  
  // 2. 下载未完成的分片
  for (let i = 0; i < totalChunks; i++) {
    if (!downloadAbortController.value) break
    
    // 跳过已下载的分片
    if (downloadedChunkIndexes.includes(i)) {
      const chunkSize = Math.min(downloadChunkSize, downloadInfo.value.size - i * downloadChunkSize)
      downloadedBytes += chunkSize
      downloadedChunkCount++
      continue
    }
    
    const start = i * downloadChunkSize
    const end = Math.min(start + downloadChunkSize, downloadInfo.value.size)
    
    try {
      // 下载分片
      const chunkData = await downloadChunk(downloadInfo.value.fileName, start, end, i)
      
      // 保存分片数据，使用数字作为键
      downloadChunks.set(i, chunkData)
      downloadedChunkIndexes.push(i)
      downloadedChunkCount++
      
      // 更新下载进度
      downloadedBytes += chunkData.byteLength
      downloadProgress.value = Math.floor((downloadedBytes / downloadInfo.value.size) * 100)
      
      // 保存下载状态
      localStorage.setItem(`download_progress_${downloadInfo.value.fileName}`, downloadProgress.value.toString())
      localStorage.setItem(downloadedChunksKey, JSON.stringify(downloadedChunkIndexes))
      
      showToast.success(`分片 ${i + 1}/${totalChunks} 下载成功`)
    } catch (error) {
      throw error
    }
  }
  
  // 3. 下载完成后合并分片并保存文件
  if (downloadAbortController.value && downloadProgress.value === 100) {
    try {
      // 合并分片
      const mergedFile = await mergeChunks(downloadInfo.value.fileName)
      
      // 保存文件
      await saveDownloadedFile(mergedFile, downloadInfo.value.fileName)
      
      showToast.success('文件下载成功')
      
      // 清除保存的进度和分片信息
      localStorage.removeItem(`download_progress_${downloadInfo.value.fileName}`)
      localStorage.removeItem(`downloaded_chunks_${downloadInfo.value.fileName}`)
      downloadChunks.clear()
    } catch (error) {
      console.error('保存文件失败:', error)
      showToast.fail('文件下载成功但保存失败')
    }
  }
}

// 格式化文件大小
const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return '0 Bytes'
  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 初始化时加载上传历史
onMounted(() => {
  // 从localStorage加载上传历史
  const savedHistory = localStorage.getItem('upload_history')
  if (savedHistory) {
    try {
      uploadHistory.push(...JSON.parse(savedHistory))
    } catch (error) {
      console.error('加载上传历史失败:', error)
    }
  }
})
</script>

<style scoped>
.upload-download-container {
  min-height: 100vh;
  padding: 20px;
  background-color: #f5f5f5;
}

.header {
  text-align: center;
  margin-bottom: 20px;
}

.content {
  max-width: 800px;
  margin: 0 auto;
}

.section {
  background: #fff;
  padding: 20px;
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.section h2 {
  margin-bottom: 15px;
  color: #333;
}

.upload-area,
.download-area {
  text-align: center;
}

.file-info,
.download-info {
  margin-top: 20px;
  text-align: left;
}

.progress-container {
  width: 100%;
  height: 20px;
  background: #e9ecef;
  border-radius: 10px;
  overflow: hidden;
  margin: 10px 0;
}

.progress-bar {
  height: 100%;
  background: #409eff;
  transition: width 0.3s;
}

.progress-text {
  text-align: center;
  margin-top: 5px;
  color: #666;
}

.upload-controls,
.download-controls {
  display: flex;
  gap: 10px;
  margin-top: 10px;
}

.history-list {
  max-height: 300px;
  overflow-y: auto;
}

.history-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.history-item:last-child {
  border-bottom: none;
}

.empty-text {
  text-align: center;
  color: #999;
  padding: 20px;
}

.download-input {
  margin-bottom: 10px;
}
</style>